package com.samoilovich.belarustoday.calendar.fragments;

import hirondelle.date4j.DateTime;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.GridView;
import android.widget.TextView;

import com.samoilovich.belarustoday.Constants;
import com.samoilovich.belarustoday.R;
import com.samoilovich.belarustoday.calendar.viewpager.InfinitePagerAdapter;
import com.samoilovich.belarustoday.calendar.viewpager.InfiniteViewPager;

/**
 * ScheduleCalendarFragment is a fragment that display calendar with dates in a mMonth. ScheduleCalendarFragment
 * can be used as embedded fragment, or as dialog fragment. <br/>
 * <br/>
 * ScheduleCalendarFragment fragment includes 4 main parts:<br/>
 * <br/>
 * 1) Month title view: show the mMonth and mYear (e.g MARCH, 2013) <br/>
 * <br/>
 * 2) Navigation arrows: to navigate to next mMonth or previous mMonth <br/>
 * <br/>
 * 3) Weekday gridview: contains only 1 row and 7 columns. To display
 * "SUN, MON, TUE, WED, THU, FRI, SAT" <br/>
 * <br/>
 * 4) An infinite view pager that allow user to swipe left/right to change
 * mMonth. This library is taken from
 * https://github.com/antonyt/InfiniteViewPager <br/>
 * <br/>
 * This infinite view pager recycles 4 fragment, each fragment contains a grid
 * view with 7 columns to display the dates in mMonth. Whenever user swipes
 * different screen, the date grid views are updated. <br/>
 * <br/>
 * ScheduleCalendarFragment fragment supports setting min/max date, selecting dates in a range,
 * setting disabled dates, highlighting mToday. It includes convenient methods to
 * work with date and string, enable or disable the navigation arrows. User can
 * also swipe left/right to change months.<br/>
 * <br/>
 */

@SuppressLint("DefaultLocale")
public class ScheduleCalendarFragment extends DialogFragment {
    public String TAG = "ScheduleCalendarFragment";

    /**
     * Weekday conventions
     */
    public static int SUNDAY = 1;
    public static int MONDAY = 2;
    public static int TUESDAY = 3;
    public static int WEDNESDAY = 4;
    public static int THURSDAY = 5;
    public static int FRIDAY = 6;
    public static int SATURDAY = 7;

    public final static int NUMBER_OF_PAGES = 4;


    /**
     * Caldroid view components
     */
    private Button mLeftArrowButton;
    private Button mRightArrowButton;
    private TextView mDialogTitleTextView;
    private TextView mMonthTitleTextView;
    private GridView mWeekdayGridView;
    private InfiniteViewPager mDateViewPager;
    private DatePageChangeListener mPageChangeListener;
    private ArrayList<DateGridFragment> mFragments;

    /**
     * Initial params key
     */
    public final static String DIALOG_TITLE = "dialogTitle";
    public final static String MONTH = "mMonth";
    public final static String YEAR = "mYear";
    public final static String SHOW_NAVIGATION_ARROWS = "mShowNavigationArrows";
    public final static String DISABLE_DATES = "disableDates";
    public final static String SELECTED_DATES = "mSelectedDates";
    public final static String CLICKED_DATE = "mClickedDate";
    public final static String MIN_DATE = "minDate";
    public final static String MAX_DATE = "maxDate";
    public final static String ENABLE_SWIPE = "enableSwipe";
    public final static String START_DAY_OF_WEEK = "mStartDayOfWeek";
    public final static String SIX_WEEKS_IN_CALENDAR = "mSixWeeksInCalendar";

    /**
     * For internal use
     */
    public final static String _MIN_DATE_TIME = "_minDateTime";
    public final static String _MAX_DATE_TIME = "_maxDateTime";
    public final static String _BACKGROUND_FOR_DATETIME_MAP = "_backgroundForDateTimeMap";
    public final static String _TEXT_COLOR_FOR_DATETIME_MAP = "_textColorForDateTimeMap";

    /**
     * Initial data
     */
    protected String mDialogTitle;
    protected int mMonth = -1;
    protected int mYear = -1;
    protected ArrayList<DateTime> disableDates = new ArrayList<DateTime>();
    protected ArrayList<DateTime> mSelectedDates = new ArrayList<DateTime>();
    protected DateTime minDateTime;
    protected DateTime maxDateTime;
    protected ArrayList<DateTime> mDateInMonthsList;
    protected DateTime mClickedDate;

    /**
     * mData belongs to ScheduleCalendarFragment
     */
    protected HashMap<String, Object> mData = new HashMap<String, Object>();

    /**
     * extraData belongs to client
     */
    protected HashMap<String, Object> extraData = new HashMap<String, Object>();

    /**
     * backgroundForDateMap holds background resource for each date
     */
    protected HashMap<DateTime, Integer> backgroundForDateTimeMap = new HashMap<DateTime, Integer>();

    /**
     * textColorForDateMap holds color for text for each date
     */
    protected HashMap<DateTime, Integer> textColorForDateTimeMap = new HashMap<DateTime, Integer>();

    /**
     * First column of calendar is Sunday
     */
    protected int startDayOfWeek = SUNDAY;

    /**
     * A calendar height is not fixed, it may have 5 or 6 rows. Set fitAllMonths
     * to true so that the calendar will always have 6 rows
     */
    private boolean sixWeeksInCalendar = true;

    /**
     * mDatePagerAdapters hold 4 adapters, meant to be reused
     */
    protected ArrayList<CalendarGridAdapter> mDatePagerAdapters = new ArrayList<CalendarGridAdapter>();

    /**
     * To control the navigation
     */
    protected boolean enableSwipe = true;
    protected boolean mShowNavigationArrows = true;

    /**
     * dateItemClickListener is fired when user click on the date cell
     */
    private OnItemClickListener dateItemClickListener;

    /**
     * dateItemLongClickListener is fired when user does a longclick on the date
     * cell
     */
    private OnItemLongClickListener dateItemLongClickListener;

    /**
     * mCalendarListener inform library client of the event happens inside
     * Caldroid
     */
    private ICalendarListener mCalendarListener;

    /**
     * Meant to be subclassed. User who wants to provide custom view, need to
     * provide custom adapter here
     */
    public CalendarGridAdapter getNewDatesGridAdapter(int month, int year) {
        return new CalendarGridAdapter(getActivity(), month, year,
                getData(), extraData);
    }

    /**
     * For client to customize the weekDayGridView
     *
     * @return
     */
    public GridView getWeekdayGridView() {
        return mWeekdayGridView;
    }

    /**
     * To let user customize the navigation buttons
     */
    public Button getLeftArrowButton() {
        return mLeftArrowButton;
    }

    public Button getRightArrowButton() {
        return mRightArrowButton;
    }

    /**
     * To let client customize mMonth title textview
     */
    public TextView getMonthTitleTextView() {
        return mMonthTitleTextView;
    }

    public void setMonthTitleTextView(TextView monthTitleTextView) {
        this.mMonthTitleTextView = monthTitleTextView;
    }

    /**
     * Get 4 adapters of the date grid views. Useful to set custom data and
     * refresh date grid view
     *
     * @return
     */
    public ArrayList<CalendarGridAdapter> getDatePagerAdapters() {
        return mDatePagerAdapters;
    }

    /**
     * mData return data belong to Caldroid
     *
     * @return
     */
    public HashMap<String, Object> getData() {
        mData.clear();
        mData.put(DISABLE_DATES, disableDates);
        mData.put(SELECTED_DATES, mSelectedDates);
        mData.put(_MIN_DATE_TIME, minDateTime);
        mData.put(_MAX_DATE_TIME, maxDateTime);
        mData.put(START_DAY_OF_WEEK, Integer.valueOf(startDayOfWeek));
        mData.put(SIX_WEEKS_IN_CALENDAR, Boolean.valueOf(sixWeeksInCalendar));
        mData.put(CLICKED_DATE, mClickedDate);

        // For internal use
        mData.put(_BACKGROUND_FOR_DATETIME_MAP, backgroundForDateTimeMap);
        mData.put(_TEXT_COLOR_FOR_DATETIME_MAP, textColorForDateTimeMap);

        return mData;
    }

    /**
     * Extra data is data belong to Client
     *
     * @return
     */
    public HashMap<String, Object> getExtraData() {
        return extraData;
    }

    /**
     * Client can set custom data in this HashMap
     *
     * @param extraData
     */
    public void setExtraData(HashMap<String, Object> extraData) {
        this.extraData = extraData;
    }

    /**
     * Set backgroundForDateMap
     */
    public void setBackgroundResourceForDates(HashMap<Date, Integer> backgroundForDateMap) {
        // Clear first
        backgroundForDateTimeMap.clear();

        if (backgroundForDateMap == null || backgroundForDateMap.size() == 0) {
            return;
        }

        for (Date date : backgroundForDateMap.keySet()) {
            Integer resource = backgroundForDateMap.get(date);
            DateTime dateTime = CalendarHelper.convertDateToDateTime(date);
            backgroundForDateTimeMap.put(dateTime, resource);
        }
    }

    public void setBackgroundResourceForDateTimes(HashMap<DateTime, Integer> backgroundForDateTimeMap) {
        this.backgroundForDateTimeMap.putAll(backgroundForDateTimeMap);
    }

    public void setBackgroundResourceForDate(int backgroundRes, Date date) {
        DateTime dateTime = CalendarHelper.convertDateToDateTime(date);
        backgroundForDateTimeMap.put(dateTime, Integer.valueOf(backgroundRes));
    }

    public void setBackgroundResourceForDateTime(int backgroundRes, DateTime dateTime) {
        backgroundForDateTimeMap.put(dateTime, Integer.valueOf(backgroundRes));
    }

    /**
     * Set textColorForDateMap
     *
     * @return
     */
    public void setTextColorForDates(HashMap<Date, Integer> textColorForDateMap) {
        // Clear first
        textColorForDateTimeMap.clear();

        if (textColorForDateMap == null || textColorForDateMap.size() == 0) {
            return;
        }

        for (Date date : textColorForDateMap.keySet()) {
            Integer resource = textColorForDateMap.get(date);
            DateTime dateTime = CalendarHelper.convertDateToDateTime(date);
            textColorForDateTimeMap.put(dateTime, resource);
        }
    }

    public void setTextColorForDateTimes(HashMap<DateTime, Integer> textColorForDateTimeMap) {
        this.textColorForDateTimeMap.putAll(textColorForDateTimeMap);
    }

    public void setTextColorForDate(int textColorRes, Date date) {
        DateTime dateTime = CalendarHelper.convertDateToDateTime(date);
        textColorForDateTimeMap.put(dateTime, Integer.valueOf(textColorRes));
    }

    public void setTextColorForDateTime(int textColorRes, DateTime dateTime) {
        textColorForDateTimeMap.put(dateTime, Integer.valueOf(textColorRes));
    }

    /**
     * Get current saved sates of the Caldroid. Useful for handling rotation
     */
    public Bundle getSavedStates() {
        Bundle bundle = new Bundle();
        bundle.putInt(MONTH, mMonth);
        bundle.putInt(YEAR, mYear);

        if (mDialogTitle != null) {
            bundle.putString(DIALOG_TITLE, mDialogTitle);
        }

        if (mSelectedDates != null && mSelectedDates.size() > 0) {
            bundle.putStringArrayList(SELECTED_DATES, CalendarHelper.convertToStringList(mSelectedDates));
        }

        if (disableDates != null && disableDates.size() > 0) {
            bundle.putStringArrayList(DISABLE_DATES, CalendarHelper.convertToStringList(disableDates));
        }

        if (minDateTime != null) {
            bundle.putString(MIN_DATE, minDateTime.format(Constants.DATE_FORMAT));
        }

        if (maxDateTime != null) {
            bundle.putString(MAX_DATE, maxDateTime.format(Constants.DATE_FORMAT));
        }

        bundle.putBoolean(SHOW_NAVIGATION_ARROWS, mShowNavigationArrows);
        bundle.putBoolean(ENABLE_SWIPE, enableSwipe);
        bundle.putInt(START_DAY_OF_WEEK, startDayOfWeek);
        bundle.putBoolean(SIX_WEEKS_IN_CALENDAR, sixWeeksInCalendar);

        return bundle;
    }

    /**
     * Save current state to bundle outState
     *
     * @param outState
     * @param key
     */
    public void saveStatesToKey(Bundle outState, String key) {
        outState.putBundle(key, getSavedStates());
    }

    /**
     * Restore current states from savedInstanceState
     *
     * @param savedInstanceState
     * @param key
     */
    public void restoreStatesFromKey(Bundle savedInstanceState, String key) {
        if (savedInstanceState != null && savedInstanceState.containsKey(key)) {
            Bundle caldroidSavedState = savedInstanceState.getBundle(key);
            setArguments(caldroidSavedState);
        }
    }

    /**
     * Restore state for dialog
     *
     * @param savedInstanceState
     * @param key
     * @param dialogTag
     */
    public void restoreDialogStatesFromKey(FragmentManager manager, Bundle savedInstanceState, String key, String dialogTag) {
        restoreStatesFromKey(savedInstanceState, key);

        ScheduleCalendarFragment existingDialog = (ScheduleCalendarFragment) manager.findFragmentByTag(dialogTag);
        if (existingDialog != null) {
            existingDialog.dismiss();
            show(manager, dialogTag);
        }
    }

    /**
     * Get current virtual position of the mMonth being viewed
     */
    public int getCurrentVirtualPosition() {
        int currentPage = mDateViewPager.getCurrentItem();
        return mPageChangeListener.getCurrent(currentPage);
    }

    /**
     * Move calendar to the specified date
     *
     * @param date
     */
    public void moveToDate(Date date) {
        moveToDateTime(CalendarHelper.convertDateToDateTime(date));
    }

    /**
     * Move calendar to specified dateTime, with animation
     *
     * @param dateTime
     */
    public void moveToDateTime(DateTime dateTime) {

        DateTime firstOfMonth = new DateTime(mYear, mMonth, 1, 0, 0, 0, 0);
        DateTime lastOfMonth = firstOfMonth.getEndOfMonth();

        // To create a swipe effect
        // Do nothing if the dateTime is in current mMonth

        // Calendar swipe left when dateTime is in the past
        if (dateTime.lt(firstOfMonth)) {
            // Get next mMonth of dateTime. When swipe left, mMonth will
            // decrease
            DateTime firstDayNextMonth = dateTime.plus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay);

            // Refresh adapters
            mPageChangeListener.setCurrentDateTime(firstDayNextMonth);
            int currentItem = mDateViewPager.getCurrentItem();
            mPageChangeListener.refreshAdapters(currentItem);

            // Swipe left
            mDateViewPager.setCurrentItem(currentItem - 1);
        }

        // Calendar swipe right when dateTime is in the future
        else if (dateTime.gt(lastOfMonth)) {
            // Get last mMonth of dateTime. When swipe right, the mMonth will
            // increase
            DateTime firstDayLastMonth = dateTime.minus(0, 1, 0, 0, 0, 0, 0,
                    DateTime.DayOverflow.LastDay);

            // Refresh adapters
            mPageChangeListener.setCurrentDateTime(firstDayLastMonth);
            int currentItem = mDateViewPager.getCurrentItem();
            mPageChangeListener.refreshAdapters(currentItem);

            // Swipe right
            mDateViewPager.setCurrentItem(currentItem + 1);
        }

    }

    /**
     * Set mMonth and mYear for the calendar. This is to avoid naive
     * implementation of manipulating mMonth and mYear. All dates within same
     * mMonth/mYear give same result
     *
     * @param date
     */
    public void setCalendarDate(Date date) {
        setCalendarDateTime(CalendarHelper.convertDateToDateTime(date));
    }

    public void setCalendarDateTime(DateTime dateTime) {
        mMonth = dateTime.getMonth();
        mYear = dateTime.getYear();

        // Notify listener
        if (mCalendarListener != null) {
            mCalendarListener.onChangeMonth(mMonth, mYear);
        }

        refreshView();
    }

    /**
     * Set calendar to previous mMonth
     */
    public void prevMonth() {
        mDateViewPager.setCurrentItem(mPageChangeListener.getCurrentPage() - 1);
    }

    /**
     * Set calendar to next mMonth
     */
    public void nextMonth() {
        mDateViewPager.setCurrentItem(mPageChangeListener.getCurrentPage() + 1);
    }

    /**
     * Clear all disable dates. Notice this does not refresh the calendar, need
     * to explicitly call refreshView()
     */
    public void clearDisableDates() {
        disableDates.clear();
    }

    /**
     * Set disableDates from ArrayList of Date
     *
     * @param disableDateList
     */
    public void setDisableDates(ArrayList<Date> disableDateList) {
        disableDates.clear();
        if (disableDateList == null || disableDateList.size() == 0) {
            return;
        }

        for (Date date : disableDateList) {
            DateTime dateTime = CalendarHelper.convertDateToDateTime(date);
            disableDates.add(dateTime);
        }

    }

    /**
     * Set disableDates from ArrayList of String. By default, the date formatter
     * is yyyy-MM-dd. For e.g 2013-12-24
     *
     * @param disableDateStrings
     */
    public void setDisableDatesFromString(ArrayList<String> disableDateStrings) {
        setDisableDatesFromString(disableDateStrings, null);
    }

    /**
     * Set disableDates from ArrayList of String with custom date format. For
     * example, if the date string is 06-Jan-2013, use date format dd-MMM-yyyy.
     * This method will refresh the calendar, it's not necessary to call
     * refreshView()
     *
     * @param disableDateStrings
     * @param dateFormat
     */
    public void setDisableDatesFromString(ArrayList<String> disableDateStrings, String dateFormat) {
        disableDates.clear();
        if (disableDateStrings == null) {
            return;
        }

        for (String dateString : disableDateStrings) {
            DateTime dateTime = CalendarHelper.getDateTimeFromString(dateString, dateFormat);
            disableDates.add(dateTime);
        }
    }

    /**
     * To clear mSelectedDates. This method does not refresh view, need to
     * explicitly call refreshView()
     */
    public void clearSelectedDates() {
        mSelectedDates.clear();
    }

    /**
     * Select the dates from fromDate to toDate. By default the background color
     * is holo_blue_light, and the text color is black. You can customize the
     * background by changing ScheduleCalendarFragment.selectedBackgroundDrawable, and
     * change the text color ScheduleCalendarFragment.selectedTextColor before call this
     * method. This method does not refresh view, need to call refreshView()
     *
     * @param fromDate
     * @param toDate
     */
    public void setSelectedDates(Date fromDate, Date toDate) {
        // Ensure fromDate is before toDate
        if (fromDate == null || toDate == null || fromDate.after(toDate)) {
            return;
        }

        mSelectedDates.clear();

        DateTime fromDateTime = CalendarHelper.convertDateToDateTime(fromDate);
        DateTime toDateTime = CalendarHelper.convertDateToDateTime(toDate);

        DateTime dateTime = fromDateTime;
        while (dateTime.lt(toDateTime)) {
            mSelectedDates.add(dateTime);
            dateTime = dateTime.plusDays(1);
        }
        mSelectedDates.add(toDateTime);
    }

    public void setSelectedDates(DateTime fromDate, DateTime toDate) {
        if (fromDate == null || toDate == null) {
            return;
        }
        // Ensure fromDate is before toDate
        Date from = CalendarHelper.convertDateTimeToDate(fromDate);
        Date to = CalendarHelper.convertDateTimeToDate(toDate);
        if (fromDate == null || toDate == null || from.after(to)) {
            return;
        }
        if (mSelectedDates != null) {
            mSelectedDates.clear();
        } else {
            mSelectedDates = new ArrayList<DateTime>();
        }
        DateTime dateTime = fromDate;
        while (dateTime.lt(toDate)) {
            mSelectedDates.add(dateTime);
            dateTime = dateTime.plusDays(1);
        }
        mSelectedDates.add(toDate);
    }

    public void setClickedDate(DateTime date) {
        mClickedDate = date;
    }

    public void setSelectedDatesAfterToday() {
        if (mSelectedDates != null) {
            mSelectedDates.clear();
        } else {
            mSelectedDates = new ArrayList<DateTime>();
        }
        mSelectedDates = CalendarHelper.getFullNextDayListAfterDate();
    }

    public void setSelectedDates(ArrayList<String> dates) {
        if (mSelectedDates != null) {
            mSelectedDates.clear();
        } else {
            mSelectedDates = new ArrayList<DateTime>();
        }
        if (dates != null) {
            for (String date : dates) {
                mSelectedDates.add(CalendarHelper.getDateTimeFromString(date, null));
            }
        }
    }

    /**
     * Convenient method to select dates from String
     *
     * @param fromDateString
     * @param toDateString
     * @param dateFormat
     * @throws java.text.ParseException
     */
    public void setSelectedDateStrings(String fromDateString, String toDateString, String dateFormat) throws ParseException {

        Date fromDate = CalendarHelper.getDateFromString(fromDateString, dateFormat);
        Date toDate = CalendarHelper.getDateFromString(toDateString, dateFormat);
        setSelectedDates(fromDate, toDate);
    }

    /**
     * Check if the navigation arrow is shown
     *
     * @return
     */
    public boolean isShowNavigationArrows() {
        return mShowNavigationArrows;
    }

    /**
     * Show or hide the navigation arrows
     *
     * @param showNavigationArrows
     */
    public void setShowNavigationArrows(boolean showNavigationArrows) {
        this.mShowNavigationArrows = showNavigationArrows;
        if (showNavigationArrows) {
            mLeftArrowButton.setVisibility(View.VISIBLE);
            mRightArrowButton.setVisibility(View.VISIBLE);
        } else {
            mLeftArrowButton.setVisibility(View.INVISIBLE);
            mRightArrowButton.setVisibility(View.INVISIBLE);
        }
    }

    /**
     * Enable / Disable swipe to navigate different months
     *
     * @return
     */
    public boolean isEnableSwipe() {
        return enableSwipe;
    }

    public void setEnableSwipe(boolean enableSwipe) {
        this.enableSwipe = enableSwipe;
        mDateViewPager.setEnabled(enableSwipe);
    }

    /**
     * Set min date. This method does not refresh view
     *
     * @param minDate
     */
    public void setMinDate(Date minDate) {
        if (minDate == null) {
            minDateTime = null;
        } else {
            minDateTime = CalendarHelper.convertDateToDateTime(minDate);
        }
    }

    public boolean isSixWeeksInCalendar() {
        return sixWeeksInCalendar;
    }

    public void setSixWeeksInCalendar(boolean sixWeeksInCalendar) {
        this.sixWeeksInCalendar = sixWeeksInCalendar;
        mDateViewPager.setSixWeeksInCalendar(sixWeeksInCalendar);
    }

    /**
     * Convenient method to set min date from String. If dateFormat is null,
     * default format is yyyy-MM-dd
     *
     * @param minDateString
     * @param dateFormat
     */
    public void setMinDateFromString(String minDateString, String dateFormat) {
        if (minDateString == null) {
            setMinDate(null);
        } else {
            minDateTime = CalendarHelper.getDateTimeFromString(minDateString, dateFormat);
        }
    }

    /**
     * Set max date. This method does not refresh view
     *
     * @param maxDate
     */
    public void setMaxDate(Date maxDate) {
        if (maxDate == null) {
            maxDateTime = null;
        } else {
            maxDateTime = CalendarHelper.convertDateToDateTime(maxDate);
        }
    }

    /**
     * Convenient method to set max date from String. If dateFormat is null,
     * default format is yyyy-MM-dd
     *
     * @param maxDateString
     * @param dateFormat
     */
    public void setMaxDateFromString(String maxDateString, String dateFormat) {
        if (maxDateString == null) {
            setMaxDate(null);
        } else {
            maxDateTime = CalendarHelper.getDateTimeFromString(maxDateString, dateFormat);
        }
    }

    /**
     * Set caldroid listener when user click on a date
     *
     * @param calendarListener
     */
    public void setCalendarListener(ICalendarListener calendarListener) {
        this.mCalendarListener = calendarListener;
    }

    /**
     * Callback to listener when date is valid (not disable, not outside of
     * min/max date)
     *
     * @return
     */
    private OnItemClickListener getDateItemClickListener() {
        if (dateItemClickListener == null) {
            dateItemClickListener = new OnItemClickListener() {
                @Override
                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                    DateTime dateTime = mDateInMonthsList.get(position);

                    if (mCalendarListener != null) {
                        if ((minDateTime != null && dateTime.lt(minDateTime)) || (maxDateTime != null && dateTime.gt(maxDateTime))
                                || (disableDates != null && disableDates.indexOf(dateTime) != -1)) {
                            return;
                        }

                        Date date = CalendarHelper.convertDateTimeToDate(dateTime);
                        mCalendarListener.onSelectDate(date, view);
                    }
                }
            };
        }

        return dateItemClickListener;
    }

    /**
     * Callback to listener when date is valid (not disable, not outside of
     * min/max date)
     *
     * @return
     */
    private OnItemLongClickListener getDateItemLongClickListener() {
        if (dateItemLongClickListener == null) {
            dateItemLongClickListener = new OnItemLongClickListener() {
                @Override
                public boolean onItemLongClick(AdapterView<?> parent,
                                               View view, int position, long id) {

                    DateTime dateTime = mDateInMonthsList.get(position);

                    if (mCalendarListener != null) {
                        if ((minDateTime != null && dateTime.lt(minDateTime)) || (maxDateTime != null && dateTime.gt(maxDateTime))
                                || (disableDates != null && disableDates
                                .indexOf(dateTime) != -1)) {
                            return false;
                        }
                        Date date = CalendarHelper
                                .convertDateTimeToDate(dateTime);
                        mCalendarListener.onLongClickDate(date, view);
                    }

                    return true;
                }
            };
        }

        return dateItemLongClickListener;
    }

    /**
     * Refresh view when parameter changes. You should always change all
     * parameters first, then call this method.
     */
    public void refreshView() {
        // If mMonth and mYear is not yet initialized, refreshView doesn't do anything
        if (mMonth == -1 || mYear == -1) {
            return;
        }

        // Refresh title view
        mMonthTitleTextView.setText(new DateTime(mYear, mMonth, 1, 0, 0, 0, 0).format("MMMM", Locale.getDefault()) + " " + mYear);

        // Refresh the date grid views
        for (CalendarGridAdapter adapter : mDatePagerAdapters) {
            // Reset caldroid data
            adapter.setData(getData());
            // Reset extra data
            adapter.setExtraData(extraData);
            // Refresh view
            adapter.notifyDataSetChanged();
        }
    }

    /**
     * Retrieve initial arguments to the fragment Data can include: mMonth, mYear,
     * mDialogTitle, mShowNavigationArrows,(String) disableDates, mSelectedDates,
     * minDate, maxDate
     */
    private void retrieveInitialArgs(Bundle savedInstanceState) {
        // Get arguments
        Bundle args = getArguments();
        if (args != null) {
            // Get mMonth, mYear
            mMonth = args.getInt(MONTH, -1);
            mYear = args.getInt(YEAR, -1);
            mDialogTitle = args.getString(DIALOG_TITLE);
            Dialog dialog = getDialog();
            dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
//			if (dialog != null) {
//				if (mDialogTitle != null) {
//					dialog.setTitle(mDialogTitle);
//				} else {
//					// Don't display title bar if user did not supply
//					// mDialogTitle
//					dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
//				}
//			}

            // Get start day of Week. Default calendar first column is SUNDAY
            startDayOfWeek = args.getInt(START_DAY_OF_WEEK, 1);
            if (startDayOfWeek > 7) {
                startDayOfWeek = startDayOfWeek % 7;
            }

            // Should show arrow
            mShowNavigationArrows = args.getBoolean(SHOW_NAVIGATION_ARROWS, true);

            // Should enable swipe to change mMonth
            enableSwipe = args.getBoolean(ENABLE_SWIPE, true);

            // Get fitAllMonths
            sixWeeksInCalendar = args.getBoolean(SIX_WEEKS_IN_CALENDAR, true);

            // Get disable dates
            ArrayList<String> disableDateStrings = args.getStringArrayList(DISABLE_DATES);
            if (disableDateStrings != null && disableDateStrings.size() > 0) {
                for (String dateString : disableDateStrings) {
                    DateTime dt = CalendarHelper.getDateTimeFromString(dateString, Constants.DATE_FORMAT);
                    disableDates.add(dt);
                }
            }

            // Get selected dates
            ArrayList<String> selectedDateStrings = args.getStringArrayList(SELECTED_DATES);
            if (selectedDateStrings != null && selectedDateStrings.size() > 0) {
                for (String dateString : selectedDateStrings) {
                    DateTime dt = CalendarHelper.getDateTimeFromString(dateString, Constants.DATE_FORMAT);
                    mSelectedDates.add(dt);
                }
            }

            // Get min date and max date
            String minDateTimeString = args.getString(MIN_DATE);
            if (minDateTimeString != null) {
                minDateTime = CalendarHelper.getDateTimeFromString(minDateTimeString, null);
            }

            String maxDateTimeString = args.getString(MAX_DATE);
            if (maxDateTimeString != null) {
                maxDateTime = CalendarHelper.getDateTimeFromString(maxDateTimeString, null);
            }

        }
        if (mMonth == -1 || mYear == -1) {
            DateTime dateTime = DateTime.today(TimeZone.getDefault());
            mMonth = dateTime.getMonth();
            mYear = dateTime.getYear();
        }
    }

    /**
     * To support faster init
     *
     * @param dialogTitle
     * @param month
     * @param year
     * @return
     */
    public static ScheduleCalendarFragment newInstance(String dialogTitle, int month, int year) {
        ScheduleCalendarFragment dialog = new ScheduleCalendarFragment();
        // Supply num input as an argument.
        Bundle args = new Bundle();
        args.putString(DIALOG_TITLE, dialogTitle);
        args.putInt(MONTH, month);
        args.putInt(YEAR, year);
        dialog.setArguments(args);

        return dialog;
    }

    /**
     * Below code fixed the issue viewpager disappears in dialog mode on
     * orientation change
     * <p/>
     * Code taken from Andy Dennie and Zsombor Erdody-Nagy
     * http://stackoverflow.com/questions/8235080/mFragments-dialogfragment
     * -and-screen-rotation
     */
    @Override
    public void onDestroyView() {
        if (getDialog() != null && getRetainInstance()) {
            getDialog().setDismissMessage(null);
        }
        super.onDestroyView();
    }

    /**
     * Setup view
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        retrieveInitialArgs(savedInstanceState);
        // To support keeping instance for dialog
        if (getDialog() != null) {
            setRetainInstance(true);
        }
        // Inflate layout
        View view = inflater.inflate(R.layout.calendar_view, container, false);
        // For the mMonthTitleTextView
        mMonthTitleTextView = (TextView) view.findViewById(R.id.calendar_month_year_textview);
        // For the left arrow button
        mLeftArrowButton = (Button) view.findViewById(R.id.calendar_left_arrow);
        mRightArrowButton = (Button) view.findViewById(R.id.calendar_right_arrow);
        mDialogTitleTextView = (TextView) view.findViewById(R.id.calendar_title);
        if (mDialogTitle != null) {
            mDialogTitleTextView.setVisibility(View.VISIBLE);
            mDialogTitleTextView.setText(mDialogTitle);
        } else {
            mDialogTitleTextView.setVisibility(View.GONE);
        }
        // Navigate to previous mMonth when user click
        mLeftArrowButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                prevMonth();
            }
        });
        // Navigate to next mMonth when user click
        mRightArrowButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                nextMonth();
            }
        });
        // Show navigation arrows depend on initial arguments
        setShowNavigationArrows(mShowNavigationArrows);
        // For the weekday gridview ("SUN, MON, TUE, WED, THU, FRI, SAT")
        mWeekdayGridView = (GridView) view.findViewById(R.id.weekday_gridview);
        WeekdayArrayAdapter weekdaysAdapter = new WeekdayArrayAdapter(getActivity(), android.R.layout.simple_list_item_1, getDaysOfWeek());
        mWeekdayGridView.setAdapter(weekdaysAdapter);
        // Setup all the pages of date grid views. These pages are recycled
        setupDateGridPages(view);
        // Refresh view
        refreshView();
        // Inform client that all views are created and not null
        // Client should perform customization for buttons and textviews here
        if (mCalendarListener != null) {
            mCalendarListener.onCalendarViewCreated();
        }

        return view;
    }

    /**
     * Setup 4 pages contain date grid views. These pages are recycled to use
     * memory efficient
     *
     * @param view
     */
    private void setupDateGridPages(View view) {
        // Get current date time
        DateTime currentDateTime = new DateTime(mYear, mMonth, 1, 0, 0, 0, 0);
        // Set to mPageChangeListener
        mPageChangeListener = new DatePageChangeListener();
        mPageChangeListener.setCurrentDateTime(currentDateTime);

        // Setup adapters for the grid views
        // Current mMonth
        CalendarGridAdapter adapter0 = getNewDatesGridAdapter(currentDateTime.getMonth(), currentDateTime.getYear());
        // Setup mDateInMonthsList
        mDateInMonthsList = adapter0.getDatetimeList();
        // Next mMonth
        DateTime nextDateTime = currentDateTime.plus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay);
        CalendarGridAdapter adapter1 = getNewDatesGridAdapter(nextDateTime.getMonth(), nextDateTime.getYear());
        // Next 2 mMonth
        DateTime next2DateTime = nextDateTime.plus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay);
        CalendarGridAdapter adapter2 = getNewDatesGridAdapter(next2DateTime.getMonth(), next2DateTime.getYear());
        // Previous mMonth
        DateTime prevDateTime = currentDateTime.minus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay);
        CalendarGridAdapter adapter3 = getNewDatesGridAdapter(prevDateTime.getMonth(), prevDateTime.getYear());
        // Add to the array of adapters
        mDatePagerAdapters.add(adapter0);
        mDatePagerAdapters.add(adapter1);
        mDatePagerAdapters.add(adapter2);
        mDatePagerAdapters.add(adapter3);

        // Set adapters to the mPageChangeListener so it can refresh the adapter
        // when page change
        mPageChangeListener.setCalendarGridAdapters(mDatePagerAdapters);
        // Setup InfiniteViewPager and InfinitePagerAdapter. The
        // InfinitePagerAdapter is responsible
        // for reuse the mFragments
        mDateViewPager = (InfiniteViewPager) view.findViewById(R.id.months_infinite_pager);
        // Set enable swipe
        mDateViewPager.setEnabled(enableSwipe);
        // Set if viewpager wrap around particular mMonth or all months (6 rows)
        mDateViewPager.setSixWeeksInCalendar(sixWeeksInCalendar);
        // Set the numberOfDaysInMonth to mDateViewPager so it can calculate the
        // height correctly
        mDateViewPager.setNumberOfDaysInMonth(mDateInMonthsList.size());
        // MonthPagerAdapter actually provides 4 real mFragments. The
        // InfinitePagerAdapter only recycles fragment provided by this
        // MonthPagerAdapter
        final MonthPagerAdapter pagerAdapter = new MonthPagerAdapter(getChildFragmentManager());

        // Provide initial data to the mFragments, before they are attached to view.
        mFragments = pagerAdapter.getFragments();
        for (int i = 0; i < NUMBER_OF_PAGES; i++) {
            DateGridFragment dateGridFragment = mFragments.get(i);
            CalendarGridAdapter adapter = mDatePagerAdapters.get(i);
            dateGridFragment.setGridAdapter(adapter);
            dateGridFragment.setOnItemClickListener(getDateItemClickListener());
            dateGridFragment.setOnItemLongClickListener(getDateItemLongClickListener());
        }

        // Setup InfinitePagerAdapter to wrap around MonthPagerAdapter
        InfinitePagerAdapter infinitePagerAdapter = new InfinitePagerAdapter(pagerAdapter);
        // Use the infinitePagerAdapter to provide data for mDateViewPager
        mDateViewPager.setAdapter(infinitePagerAdapter);
        // Setup mPageChangeListener
        mDateViewPager.setOnPageChangeListener(mPageChangeListener);
    }

    /**
     * To display the week day title
     *
     * @return "SUN, MON, TUE, WED, THU, FRI, SAT"
     */
    private ArrayList<String> getDaysOfWeek() {
        ArrayList<String> list = new ArrayList<String>();

        // 17 Feb 2013 is Sunday
        DateTime sunday = new DateTime(2013, 2, 17, 0, 0, 0, 0);
        DateTime nextDay = sunday.plusDays(startDayOfWeek - SUNDAY);

        for (int i = 0; i < 7; i++) {
            list.add(nextDay.format("WWW", Locale.getDefault()).toUpperCase());
            nextDay = nextDay.plusDays(1);
        }

        return list;
    }

    /**
     * DatePageChangeListener refresh the date grid views when user swipe the
     * calendar
     *
     * @author thomasdao
     */
    public class DatePageChangeListener implements OnPageChangeListener {
        private int currentPage = InfiniteViewPager.OFFSET;
        private DateTime currentDateTime;
        private ArrayList<CalendarGridAdapter> mCalendarGridAdapters;

        /**
         * Return currentPage of the mDateViewPager
         *
         * @return
         */
        public int getCurrentPage() {
            return currentPage;
        }

        public void setCurrentPage(int currentPage) {
            this.currentPage = currentPage;
        }

        /**
         * Return currentDateTime of the selected page
         *
         * @return
         */
        public DateTime getCurrentDateTime() {
            return currentDateTime;
        }

        public void setCurrentDateTime(DateTime dateTime) {
            this.currentDateTime = dateTime;
            setCalendarDateTime(currentDateTime);
        }

        /**
         * Return 4 adapters
         *
         * @return
         */
        public ArrayList<CalendarGridAdapter> getCalendarGridAdapters() {
            return mCalendarGridAdapters;
        }

        public void setCalendarGridAdapters(ArrayList<CalendarGridAdapter> calendarGridAdapters) {
            this.mCalendarGridAdapters = calendarGridAdapters;
        }

        /**
         * Return virtual next position
         *
         * @param position
         * @return
         */
        private int getNext(int position) {
            return (position + 1) % ScheduleCalendarFragment.NUMBER_OF_PAGES;
        }

        /**
         * Return virtual previous position
         *
         * @param position
         * @return
         */
        private int getPrevious(int position) {
            return (position + 3) % ScheduleCalendarFragment.NUMBER_OF_PAGES;
        }

        /**
         * Return virtual current position
         *
         * @param position
         * @return
         */
        public int getCurrent(int position) {
            return position % ScheduleCalendarFragment.NUMBER_OF_PAGES;
        }

        @Override
        public void onPageScrollStateChanged(int position) {
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        public void refreshAdapters(int position) {
            // Get adapters to refresh
            CalendarGridAdapter currentAdapter = mCalendarGridAdapters.get(getCurrent(position));
            CalendarGridAdapter prevAdapter = mCalendarGridAdapters.get(getPrevious(position));
            CalendarGridAdapter nextAdapter = mCalendarGridAdapters.get(getNext(position));

            if (position == currentPage) {
                // Refresh current adapter
                currentAdapter.setAdapterDateTime(currentDateTime);
                currentAdapter.notifyDataSetChanged();
                // Refresh previous adapter
                prevAdapter.setAdapterDateTime(currentDateTime.minus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay));
                prevAdapter.notifyDataSetChanged();
                // Refresh next adapter
                nextAdapter.setAdapterDateTime(currentDateTime.plus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay));
                nextAdapter.notifyDataSetChanged();
            }
            // Detect if swipe right or swipe left
            // Swipe right
            else if (position > currentPage) {
                // Update current date time to next mMonth
                currentDateTime = currentDateTime.plus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay);
                // Refresh the adapter of next gridview
                nextAdapter.setAdapterDateTime(currentDateTime.plus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay));
                nextAdapter.notifyDataSetChanged();
            }
            // Swipe left
            else {
                // Update current date time to previous mMonth
                currentDateTime = currentDateTime.minus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay);
                // Refresh the adapter of previous gridview
                prevAdapter.setAdapterDateTime(currentDateTime.minus(0, 1, 0, 0, 0, 0, 0, DateTime.DayOverflow.LastDay));
                prevAdapter.notifyDataSetChanged();
            }
            // Update current page
            currentPage = position;
        }

        /**
         * Refresh the mFragments
         */
        @Override
        public void onPageSelected(int position) {
            refreshAdapters(position);

            // Update current date time of the selected page
            setCalendarDateTime(currentDateTime);

            // Update all the dates inside current mMonth
            CalendarGridAdapter currentAdapter = mCalendarGridAdapters.get(position % ScheduleCalendarFragment.NUMBER_OF_PAGES);

            // Refresh mDateInMonthsList
            mDateInMonthsList.clear();
            mDateInMonthsList.addAll(currentAdapter.getDatetimeList());
        }

    }

}
